use tower::{retry::{Policy,budget::Budget},ServiceBuilder, ServiceExt, BoxError, service_fn, Service, Layer};
use tonic::codegen::Context;
use tokio::macros::support::Poll;
use std::pin::Pin;
use std::future::Future;
use rand::Rng;
use futures_util::future;

async fn handle(request: String) -> Result<String, BoxError> {
    Ok(request)
}

#[tokio::main]
async fn main() {
    let svc = ServiceBuilder::new()
        .timeout(std::time::Duration::from_secs(1))
        .concurrency_limit(100)
        .buffer(10)
        // .service(RequestMid{ inner: () });
        .layer(RequestMidLay{})
        .retry(ReTryManage(3) )
        .service_fn(handle);
    let response = svc.oneshot("hello world".to_string()).await.unwrap();
    wd_log::log_debug_ln!("response:{}",response);
}

struct RequestMid<S> {
    inner:S
}
impl<S> Service<String> for RequestMid<S>
    where
        S: Service<String>,
        S::Error: Into<crate::BoxError>,
{
    type Response = S::Response;
    type Error = S::Error;
    type Future = S::Future;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        match self.inner.poll_ready(cx) {
            Poll::Pending => Poll::Pending,
            Poll::Ready(r) => Poll::Ready(r),
        }
    }

    fn call(&mut self, request: String) -> Self::Future {
        let rid = rand::thread_rng().gen_range(100000..999999);
        let request = format!("[{}]:{}",rid,request);
        self.inner.call(request)
    }
}

struct RequestMidLay {
}
impl<T> Layer<T> for RequestMidLay{
    type Service = RequestMid<T>;
    fn layer(&self, t:T) -> Self::Service {
        RequestMid{inner:t}
    }
}

#[derive(Clone)]
struct ReTryManage(i32);
type Req = String;
type Res = String;
impl<E> Policy<Req, Res, E> for ReTryManage {
    type Future = future::Ready<Self>;

    fn retry(&self, req: &Req, result: Result<&Res, &E>) -> Option<Self::Future> {
        match result {
            Ok(s) => {
                let s = &s.as_bytes()[1..7];
                let s = String::from_utf8_lossy(s);
                let rid = s.parse::<usize>().unwrap();
                wd_log::log_debug_ln!("request id is {}",rid);

                None
            },
            Err(_) => {
                if self.0 > 0 {
                    Some(future::ready(ReTryManage(self.0 - 1)))
                } else {
                    None
                }
            }
        }
    }

    fn clone_request(&self, req: &Req) -> Option<Req> {
        // Some(req.clone())
        None
    }
}